package cn.mazexal.exambg.services;

import cn.hutool.core.convert.Convert;
import cn.mazexal.exambg.common.models.Tuple;
import cn.mazexal.exambg.utils.RedisUtil;
import jakarta.annotation.Resource;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @author: Mazexal
 * Date: 2025/5/14
 */
@Service
public class RedisService {
    private static final Long RELEASE_LOCK_SUCCESS_RESULT = 1L;

    @Resource
    private RedisUtil redisUtil;

    /**
     * 获取 key 值
     *
     * @param key
     * @return
     */

    public String get(String key) {
        return redisUtil.get(key);
    }

    /**
     * 批量获取 key 值
     *
     * @param keys
     * @return
     */

    public List<String> batchGet(List<String> keys) {
        if (CollectionUtils.isNotEmpty(keys)) {
            List<String> values = new ArrayList<>();
            for (String key : keys) {
                String value = get(key);
                if (StringUtils.isNotBlank(value)) {
                    values.add(value);
                }
            }
            return values;
        }

        return Collections.EMPTY_LIST;
    }

    /**
     * 设置 key 值
     *
     * @param key
     * @param value
     * @param timeout
     * @param unit
     */

    public void set(String key, String value, long timeout, TimeUnit unit) {
        redisUtil.setEx(key, value, timeout, unit);
    }

    /**
     * 设置 key 值
     *
     * @param key
     * @param value
     */

    public void set(String key, String value) {
        redisUtil.set(key, value);
    }

    /**
     * key 不存在时写入
     *
     * @param key
     * @param value
     * @return
     */

    public boolean setIfAbsent(String key, String value) {
        return redisUtil.setIfAbsent(key, value);
    }

    /**
     * key 不存在时写入并设置过期时间
     *
     * @param key
     * @param value
     * @param timeout
     * @param unit
     * @return
     */

    public boolean setIfAbsent(String key, String value, long timeout, TimeUnit unit) {
        boolean isSet = redisUtil.setIfAbsent(key, value);
        if (isSet) {
            redisUtil.expire(key, timeout, unit);
            return true;
        }
        return false;
    }

    /**
     * 增长并设置过期时间
     *
     * @param key
     * @param delta
     * @param timeout
     * @param unit
     * @return
     */

    public Long increaseAndExpire(String key, long delta, long timeout, TimeUnit unit) {
        long value = redisUtil.incrBy(key, delta);
        redisUtil.expire(key, timeout, unit);
        return value;
    }

    /**
     * 判断 key 是否存在
     *
     * @param key
     * @return
     */

    public Boolean hasKey(String key) {
        return redisUtil.hasKey(key);
    }

    /**
     * 获取哈希键值
     *
     * @param key
     * @param field
     * @return
     */

    public Object hashGet(String key, String field) {
        return redisUtil.hGet(key, field);
    }

    /**
     * 获取所有哈希键值
     *
     * @param key
     * @return
     */

    public Map<Object, Object> hashGetAll(String key) {
        return redisUtil.hGetAll(key);
    }

    /**
     * 哈希表设值
     *
     * @param key
     * @param field
     * @param value
     * @return
     */

    public void hashPut(String key, String field, String value) {
        redisUtil.hPut(key, field, value);
    }

    /**
     * 哈希表大小
     *
     * @param key
     * @return
     */

    public Long hashSize(String key) {
        return redisUtil.hSize(key);
    }

    /**
     * 哈希键值
     *
     * @param key
     * @return
     */

    public Set<Object> hashKeys(String key) {
        return redisUtil.hKeys(key);
    }

    /**
     * 哈希表设值 不存在时写入
     *
     * @param key
     * @param field
     * @param value
     * @return
     */

    public boolean hashPutIfAbsent(String key, String field, String value) {
        return redisUtil.hPutIfAbsent(key, field, value);
    }


    /**
     * 哈希表增长
     *
     * @param key
     * @param field
     * @param delta
     * @return
     */

    public Long hashIncreaseLong(String key, String field, long delta) {
        return redisUtil.hIncrBy(key, field, delta);
    }


    public Boolean delKey(String key) {
        return redisUtil.delete(key);
    }

    /**
     * 批量删除 key
     *
     * @param keys
     * @return
     */

    public void delKeys(List<String> keys) {
        for (String key : keys) {
            if (StringUtils.isNotBlank(key)) {
                redisUtil.delete(key);
            }
        }
    }


    public void expire(String key, long expire) {
        redisUtil.expire(key, expire, TimeUnit.MILLISECONDS);
    }

    /**
     * 获取 keys
     *
     * @param pattern
     * @return
     */

    public List<String> getKeys(String pattern) {
        return new ArrayList<>(redisUtil.keys(pattern));
    }

    /**
     * 判断哈希键值
     *
     * @param key
     * @param field
     * @return
     */

    public Boolean hashExists(String key, String field) {
        return redisUtil.hExists(key, field);
    }

    /**
     * 以秒为单位，返回给定key的剩余生存时间
     */

    public long getTtl(String key) {
        return redisUtil.getTtl(key);
    }

    /**
     * 有序集合增加值
     *
     * @param key
     * @param value
     * @param score
     * @return
     */

    public Boolean zAdd(String key, String value, double score) {
        return redisUtil.zAdd(key, value, score);
    }

    /**
     * 移除集合元素
     *
     * @param key
     * @param values
     * @return
     */

    public Long zRemove(String key, Object... values) {
        return redisUtil.zRemove(key, values);
    }

    /**
     * 集合增量
     *
     * @param key
     * @param value
     * @param delta
     * @return
     */

    public Double zIncrementScore(String key, String value, double delta) {
        return redisUtil.zIncrementScore(key, value, delta);
    }

    /**
     * 获取集合元素, 并且把score值也获取
     *
     * @param key
     * @param start
     * @param end
     * @return
     */

    public Set<Tuple> zRangeWithScores(String key, long start, long end) {
        Set<ZSetOperations.TypedTuple<String>> sets = redisUtil.zRangeWithScores(key, start, end);
        if (CollectionUtils.isNotEmpty(sets)) {
            Set<Tuple> tuples = new HashSet<>();
            for (ZSetOperations.TypedTuple<String> typedTuple : sets) {
                Tuple tuple = new Tuple();
                tuple.setElement(typedTuple.getValue());
                tuple.setScore(typedTuple.getScore());
                tuples.add(tuple);
            }
            return tuples;
        }
        return Collections.EMPTY_SET;
    }

    /**
     * 获取集合元素，按从大到小
     *
     * @param key
     * @param start
     * @param end
     * @return
     */

    public Set<Tuple> zReverseRangeWithScores(String key, long start, long end) {
        Set<ZSetOperations.TypedTuple<String>> sets = redisUtil.zReverseRangeWithScores(key, start, end);
        if (CollectionUtils.isNotEmpty(sets)) {
            Set<Tuple> tuples = new HashSet<>();
            for (ZSetOperations.TypedTuple<String> typedTuple : sets) {
                Tuple tuple = new Tuple();
                tuple.setElement(typedTuple.getValue());
                tuple.setScore(typedTuple.getScore());
                tuples.add(tuple);
            }
            return tuples;
        }
        return Collections.EMPTY_SET;
    }

    /**
     * 获取集合的元素, 从小到大排序
     *
     * @param key
     * @param start 开始位置
     * @param end   结束位置, -1查询所有
     * @return
     */

    public Set<String> zRange(String key, long start, long end) {
        return redisUtil.zRange(key, start, end);
    }

    /**
     * 获取结合元素值
     *
     * @param key
     * @param value
     * @return
     */

    public Double zScore(String key, String value) {
        return redisUtil.zScore(key, value);
    }

    /**
     * 获取排名
     *
     * @param key
     * @param value
     * @return
     */

    public Long zRank(String key, String value) {
        return redisUtil.zRank(key, value);
    }

    /**
     * 集合统计
     *
     * @param key
     * @param min
     * @param max
     * @return
     */

    public Long zCount(String key, double min, double max) {
        return redisUtil.zCount(key, min, max);
    }

    /**
     * 集合统计
     *
     * @param key
     * @return
     */

    public Long zCard(String key) {
        return redisUtil.zZCard(key);
    }

    /**
     * 获取排名倒序
     *
     * @param key
     * @param value
     * @return
     */

    public Long zRevRank(String key, String value) {
        return redisUtil.zReverseRank(key, value);
    }

    /**
     * 执行 Lua 脚本返回
     *
     * @param script
     * @param keys
     * @param args
     * @return
     */

    public long luaLong(String script, List<String> keys, List<String> args) {
        DefaultRedisScript<String> redisScript = new DefaultRedisScript<>();
        redisScript.setScriptText(script);
        redisScript.setResultType(String.class);

        String res = redisUtil.getRedisTemplate().execute(redisScript, new StringRedisSerializer(),
                new StringRedisSerializer(), keys, args.toArray());
        if (StringUtils.isBlank(res)) {
            return 0L;
        }
        return Convert.toLong(res);
    }

    /**
     * 获取锁并设置过期时间 （原子操作）
     *
     * @param key
     * @param value
     * @param timeout
     * @param unit
     * @return
     */

    public boolean tryLock(String key, String value, long timeout, TimeUnit unit) {
        return redisUtil.setIfAbsent(key, value, timeout, unit);
    }

    /**
     * 释放锁
     *
     * @param key
     * @param value
     * @return
     */

    public boolean releaseLock(String key, String value) {
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return " +
                "0 end ";
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
        redisScript.setScriptText(script);
        redisScript.setResultType(Long.class);
        return RELEASE_LOCK_SUCCESS_RESULT.equals(redisUtil.getRedisTemplate().execute(redisScript,
                Collections.singletonList(key), value));
    }

    /**
     * set添加元素
     *
     * @param key
     * @param values
     * @return
     */

    public Long sAdd(String key, String... values) {
        return redisUtil.sAdd(key, values);
    }


    /**
     * 移除并返回集合的一个随机元素
     *
     * @param key
     * @return
     */

    public String sPop(String key) {
        return redisUtil.sPop(key);
    }

    /**
     * 将元素value从一个集合移到另一个集合
     *
     * @param key
     * @param value
     * @param destKey
     * @return
     */

    public Boolean sMove(String key, String value, String destKey) {
        return redisUtil.sMove(key, value, destKey);
    }

    /**
     * 获取集合的大小
     *
     * @param key
     * @return
     */

    public Long sSize(String key) {
        return redisUtil.sSize(key);
    }

    /**
     * 判断集合是否包含value
     *
     * @param key
     * @param value
     * @return
     */

    public Boolean sIsMember(String key, Object value) {
        return redisUtil.sIsMember(key, value);
    }

    /**
     * 获取两个集合的交集
     *
     * @param key
     * @param otherKey
     * @return
     */

    public Set<String> sIntersect(String key, String otherKey) {
        return redisUtil.sIntersect(key, otherKey);
    }

    /**
     * 获取key集合与多个集合的交集
     *
     * @param key
     * @param otherKeys
     * @return
     */

    public Set<String> sIntersect(String key, Collection<String> otherKeys) {
        return redisUtil.sIntersect(key, otherKeys);
    }

    /**
     * key集合与otherKey集合的交集存储到destKey集合中
     *
     * @param key
     * @param otherKey
     * @param destKey
     * @return
     */

    public Long sIntersectAndStore(String key, String otherKey, String destKey) {
        return redisUtil.sIntersectAndStore(key, otherKey, destKey);
    }

    /**
     * key集合与多个集合的交集存储到destKey集合中
     *
     * @param key
     * @param otherKeys
     * @param destKey
     * @return
     */

    public Long sIntersectAndStore(String key, Collection<String> otherKeys,
                                   String destKey) {
        return redisUtil.sIntersectAndStore(key, otherKeys, destKey);
    }

    /**
     * 获取两个集合的并集
     *
     * @param key
     * @param otherKeys
     * @return
     */

    public Set<String> sUnion(String key, String otherKeys) {
        return redisUtil.sUnion(key, otherKeys);
    }

    /**
     * 获取key集合与多个集合的并集
     *
     * @param key
     * @param otherKeys
     * @return
     */

    public Set<String> sUnion(String key, Collection<String> otherKeys) {
        return redisUtil.sUnion(key, otherKeys);
    }

    /**
     * key集合与otherKey集合的并集存储到destKey中
     *
     * @param key
     * @param otherKey
     * @param destKey
     * @return
     */

    public Long sUnionAndStore(String key, String otherKey, String destKey) {
        return redisUtil.sUnionAndStore(key, otherKey, destKey);
    }

    /**
     * key集合与多个集合的并集存储到destKey中
     *
     * @param key
     * @param otherKeys
     * @param destKey
     * @return
     */

    public Long sUnionAndStore(String key, Collection<String> otherKeys,
                               String destKey) {
        return redisUtil.sUnionAndStore(key, otherKeys, destKey);
    }

    /**
     * 获取两个集合的差集
     *
     * @param key
     * @param otherKey
     * @return
     */

    public Set<String> sDifference(String key, String otherKey) {
        return redisUtil.sDifference(key, otherKey);
    }

    /**
     * 获取key集合与多个集合的差集
     *
     * @param key
     * @param otherKeys
     * @return
     */

    public Set<String> sDifference(String key, Collection<String> otherKeys) {
        return redisUtil.sDifference(key, otherKeys);
    }

    /**
     * key集合与otherKey集合的差集存储到destKey中
     *
     * @param key
     * @param otherKey
     * @param destKey
     * @return
     */

    public Long sDifference(String key, String otherKey, String destKey) {
        return redisUtil.sDifference(key, otherKey, destKey);
    }

    /**
     * key集合与多个集合的差集存储到destKey中
     *
     * @param key
     * @param otherKeys
     * @param destKey
     * @return
     */

    public Long sDifference(String key, Collection<String> otherKeys,
                            String destKey) {
        return redisUtil.sDifference(key, otherKeys, destKey);
    }

    /**
     * 获取集合所有元素
     *
     * @param key
     * @return
     */

    public Set<String> setMembers(String key) {
        return redisUtil.setMembers(key);
    }

    /**
     * 随机获取集合中的一个元素
     *
     * @param key
     * @return
     */

    public String sRandomMember(String key) {
        return redisUtil.sRandomMember(key);
    }

    /**
     * 随机获取集合中count个元素
     *
     * @param key
     * @param count
     * @return
     */

    public List<String> sRandomMembers(String key, long count) {
        return redisUtil.sRandomMembers(key, count);
    }

    /**
     * 随机获取集合中count个元素并且去除重复的
     *
     * @param key
     * @param count
     * @return
     */

    public Set<String> sDistinctRandomMembers(String key, long count) {
        return redisUtil.sDistinctRandomMembers(key, count);
    }

    /**
     * 返回所有结合成员
     *
     * @param key
     * @return
     */

    public Set<String> sMembers(String key) {
        return redisUtil.sMembers(key);
    }

    public Long getIncreaseId(String key) {
        return redisUtil.incrBy(key, 1);
    }
}
